package com.ingenico.insider.model;

import java.util.Enumeration;
import java.util.Vector;

import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;

import com.ingenico.insider.nodes.PSampleChannel;

public class PCurveListModel extends AbstractListModel {
    /**
	 * Generated Serial Version UID
	 */
	private static final long serialVersionUID = 450573161230401696L;

	public static final int INTERVAL_MOVED = 3;

	private Vector<PSampleChannel> delegate = new Vector<PSampleChannel>();

    /**
     * <code>PCurveListModel</code> subclasses must call this method
     * <b>after</b> one element was moved from the model. 
     * <code>index0</code> is the old index of the element and
     * <code>index1</code> is the new end point of the element
     * that's been moved.
     * 
     * @param source the <code>ListModel</code> that changed, typically "this"
     * @param index0 one end of the removed interval,
     *               including <code>index0</code>
     * @param index1 the other end of the removed interval,
     *               including <code>index1</code>
     * @see EventListenerList
     * @see DefaultListModel
     */
	protected void fireElementMoved(Object source, int index0, int index1)
	{
		Object[] listeners = listenerList.getListenerList();
		ListDataEvent e = null;

		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == PCurveListDataListener.class) {
				if (e == null) {
					e = new ListDataEvent(source, PCurveListModel.INTERVAL_MOVED, index0, index1);
				}
				((PCurveListDataListener)listeners[i+1]).intervalMoved(e);
			}	       
		}
	}


	/**
	 * Returns the number of components in this list.
	 * <p>
	 * This method is identical to <code>size</code>, which implements the 
	 * <code>List</code> interface defined in the 1.2 Collections framework.
	 * This method exists in conjunction with <code>setSize</code> so that
	 * <code>size</code> is identifiable as a JavaBean property.
	 *
	 * @return  the number of components in this list
	 * @see #size()
	 */
	public int getSize() {
		return delegate.size();
	}

	/**
	 * Returns the component at the specified index.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>get(int)</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 * @param      index   an index into this list
	 * @return     the component at the specified index
	 * @exception  ArrayIndexOutOfBoundsException  if the <code>index</code> 
	 *             is negative or greater than the current size of this 
	 *             list
	 * @see #get(int)
	 */
	public PSampleChannel getElementAt(int index) {
		return delegate.elementAt(index);
	}

	/**
	 * Copies the components of this list into the specified array.
	 * The array must be big enough to hold all the curves in this list, 
	 * else an <code>IndexOutOfBoundsException</code> is thrown.
	 *
	 * @param   anArray   the array into which the components get copied
	 * @see Vector#copyInto(Object[])
	 */
	public void copyInto(PSampleChannel anArray[]) {
		delegate.copyInto(anArray);
	}

	/**
	 * Trims the capacity of this list to be the list's current size.
	 * 
	 * @see Vector#trimToSize()
	 */
	public void trimToSize() {
		delegate.trimToSize();
	}

	/**
	 * Increases the capacity of this list, if necessary, to ensure 
	 * that it can hold at least the number of components specified by 
	 * the minimum capacity argument. 
	 *
	 * @param   minCapacity   the desired minimum capacity
	 * @see Vector#ensureCapacity(int)
	 */
	public void ensureCapacity(int minCapacity) {
		delegate.ensureCapacity(minCapacity);
	}

	/**
	 * Sets the size of this list. 
	 *
	 * @param   newSize   the new size of this list
	 * @see Vector#setSize(int)
	 */
	public void setSize(int newSize) {
		int oldSize = delegate.size();
		delegate.setSize(newSize);
		if (oldSize > newSize) {
			fireIntervalRemoved(this, newSize, oldSize-1);
		}
		else if (oldSize < newSize) {
			fireIntervalAdded(this, oldSize, newSize-1);
		}
	}

	/**
	 * Returns the current capacity of this list.
	 *
	 * @return  the current capacity
	 * @see Vector#capacity()
	 */
	public int capacity() {
		return delegate.capacity();
	}

	/**
	 * Returns the number of components in this list.
	 *
	 * @return  the number of components in this list
	 * @see Vector#size()
	 */
	public int size() {
		return delegate.size();
	}

	/**
	 * Tests whether this list has any components.
	 *
	 * @return  <code>true</code> if and only if this list has 
	 *          no components, that is, its size is zero;
	 *          <code>false</code> otherwise
	 * @see Vector#isEmpty()
	 */
	public boolean isEmpty() {
		return delegate.isEmpty();
	}

	/**
	 * Returns an enumeration of the components of this list.
	 *
	 * @return  an enumeration of the components of this list
	 * @see Vector#elements()
	 */
	public Enumeration<PSampleChannel> elements() {
		return delegate.elements();
	}

	/**
	 * Tests whether the specified curve is a component in this list.
	 *
	 * @param   elem   a curve
	 * @return  <code>true</code> if the specified curve 
	 *          is the same as a component in this list
	 * @see Vector#contains(Object)
	 */
	public boolean contains(PSampleChannel elem) {
		return delegate.contains(elem);
	}

	/**
	 * Searches for the first occurrence of <code>elem</code>.
	 *
	 * @param   elem   a curve
	 * @return  the index of the first occurrence of the argument in this
	 *          list; returns <code>-1</code> if the curve is not found
	 * @see Vector#indexOf(Object)
	 */
	public int indexOf(PSampleChannel elem) {
		return delegate.indexOf(elem);
	}

	/**
	 * Searches for the first occurrence of <code>elem</code>, beginning 
	 * the search at <code>index</code>. 
	 *
	 * @param   elem    an desired component
	 * @param   index   the index from which to begin searching
	 * @return  the index where the first occurrence of <code>elem</code>
	 *          is found after <code>index</code>; returns <code>-1</code>
	 *          if the <code>elem</code> is not found in the list
	 * @see Vector#indexOf(Object,int)
	 */
	public int indexOf(PSampleChannel elem, int index) {
		return delegate.indexOf(elem, index);
	}

	/**
	 * Returns the index of the last occurrence of <code>elem</code>.
	 *
	 * @param   elem   the desired component
	 * @return  the index of the last occurrence of <code>elem</code>
	 *          in the list; returns <code>-1</code> if the curve is not found
	 * @see Vector#lastIndexOf(Object)
	 */
	public int lastIndexOf(PSampleChannel elem) {
		return delegate.lastIndexOf(elem);
	}

	/**
	 * Searches backwards for <code>elem</code>, starting from the 
	 * specified index, and returns an index to it. 
	 *
	 * @param  elem    the desired component
	 * @param  index   the index to start searching from
	 * @return the index of the last occurrence of the <code>elem</code> 
	 *          in this list at position less than <code>index</code>;
	 *          returns <code>-1</code> if the curve is not found
	 * @see Vector#lastIndexOf(Object,int)
	 */
	public int lastIndexOf(PSampleChannel elem, int index) {
		return delegate.lastIndexOf(elem, index);
	}

	/**
	 * Returns the component at the specified index.
	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index
	 * is negative or not less than the size of the list.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>get(int)</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 *
	 * @param      index   an index into this list
	 * @return     the component at the specified index
	 * @see #get(int)
	 * @see Vector#elementAt(int)
	 */
	public PSampleChannel elementAt(int index) {
		return delegate.elementAt(index);
	}

	/**
	 * Returns the first component of this list.
	 * Throws a <code>NoSuchElementException</code> if this
	 * vector has no components.
	 * @return     the first component of this list
	 * @see Vector#firstElement()
	 */
	public PSampleChannel firstElement() {
		return delegate.firstElement();
	}

	/**
	 * Returns the last component of the list.
	 * Throws a <code>NoSuchElementException</code> if this vector
	 * has no components.
	 *
	 * @return  the last component of the list
	 * @see Vector#lastElement()
	 */
	public PSampleChannel lastElement() {
		return delegate.lastElement();
	}

	/**
	 * Sets the component at the specified <code>index</code> of this 
	 * list to be the specified curve. The previous component at that 
	 * position is discarded.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index 
	 * is invalid.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>set(int,PSampleChannel)</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 *
	 * @param      curve   what the component is to be set to
	 * @param      index   the specified index
	 * @see #set(int,PSampleChannel)
	 * @see Vector#setElementAt(Object,int)
	 */
	public void setElementAt(PSampleChannel curve, int index) {
		delegate.setElementAt(curve, index);
		fireContentsChanged(this, index, index);
	}

	/**
	 * Deletes the component at the specified index.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index 
	 * is invalid.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>remove(int)</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 *
	 * @param      index   the index of the curve to remove
	 * @see #remove(int)
	 * @see Vector#removeElementAt(int)
	 */
	public void removeElementAt(int index) {
		delegate.removeElementAt(index);
		fireIntervalRemoved(this, index, index);
	}

	/**
	 * Inserts the specified curve as a component in this list at the 
	 * specified <code>index</code>.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index 
	 * is invalid.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>add(int,PSampleChannel)</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 *
	 * @param      curve   the component to insert
	 * @param      index   where to insert the new component
	 * @exception  ArrayIndexOutOfBoundsException  if the index was invalid
	 * @see #add(int,PSampleChannel)
	 * @see Vector#insertElementAt(Object,int)
	 */
	public void insertElementAt(PSampleChannel curve, int index) {
		delegate.insertElementAt(curve, index);
		fireIntervalAdded(this, index, index);
	}

	/**
	 * Adds the specified component to the end of this list. 
	 *
	 * @param   curve the component to be added
	 * @see Vector#addElement(Object)
	 */
	public void addElement(PSampleChannel curve) {
		int index = delegate.size();
		delegate.addElement(curve);
		fireIntervalAdded(this, index, index);
	}

	/**
	 * Removes the first (lowest-indexed) occurrence of the argument 
	 * from this list.
	 *
	 * @param   curve the component to be removed
	 * @return  <code>true</code> if the argument was a component of this
	 *          list; <code>false</code> otherwise
	 * @see Vector#removeElement(Object)
	 */
	public boolean removeElement(PSampleChannel curve) {
		int index = indexOf(curve);
		boolean rv = delegate.removeElement(curve);
		if (index >= 0) {
			fireIntervalRemoved(this, index, index);
		}
		return rv;
	}


	/**
	 * Removes all components from this list and sets its size to zero.
	 * <blockquote>
	 * <b>Note:</b> Although this method is not deprecated, the preferred
	 *    method to use is <code>clear</code>, which implements the 
	 *    <code>List</code> interface defined in the 1.2 Collections framework.
	 * </blockquote>
	 *
	 * @see #clear()
	 * @see Vector#removeAllElements()
	 */
	public void removeAllElements() {
		int index1 = delegate.size()-1;
		delegate.removeAllElements();
		if (index1 >= 0) {
			fireIntervalRemoved(this, 0, index1);
		}
	}


	/**
	 * Returns a string that displays and identifies this
	 * curve's properties.
	 *
	 * @return a String representation of this curve
	 */
	public String toString() {
		return delegate.toString();
	}


	/* The remaining methods are included for compatibility with the
	 * Java 2 platform Vector class.
	 */

	/**
	 * Returns an array containing all of the elements in this list in the
	 * correct order.
	 *
	 * @return an array containing the elements of the list
	 * @see Vector#toArray()
	 */
	public PSampleChannel[] toArray() {
		PSampleChannel[] rv = new PSampleChannel[delegate.size()];
		delegate.copyInto(rv);
		return rv;
	}

	/**
	 * Returns the element at the specified position in this list.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code>
	 * if the index is out of range
	 * (<code>index &lt; 0 || index &gt;= size()</code>).
	 *
	 * @param index index of element to return
	 */
	public PSampleChannel get(int index) {
		return delegate.elementAt(index);
	}

	/**
	 * Replaces the element at the specified position in this list with the
	 * specified element.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code>
	 * if the index is out of range
	 * (<code>index &lt; 0 || index &gt;= size()</code>).
	 *
	 * @param index index of element to replace
	 * @param element element to be stored at the specified position
	 * @return the element previously at the specified position
	 */
	public PSampleChannel set(int index, PSampleChannel element) {
		PSampleChannel rv = delegate.elementAt(index);
		delegate.setElementAt(element, index);
		fireContentsChanged(this, index, index);
		return rv;
	}

	/**
	 * Inserts the specified element at the specified position in this list.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the
	 * index is out of range
	 * (<code>index &lt; 0 || index &gt; size()</code>).
	 *
	 * @param index index at which the specified element is to be inserted
	 * @param element element to be inserted
	 */
	public void add(int index, PSampleChannel element) {
		delegate.insertElementAt(element, index);
		fireIntervalAdded(this, index, index);
	}

	/**
	 * Removes the element at the specified position in this list.
	 * Returns the element that was removed from the list.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code>
	 * if the index is out of range
	 * (<code>index &lt; 0 || index &gt;= size()</code>).
	 *
	 * @param index the index of the element to removed
	 */
	public PSampleChannel remove(int index) {
		PSampleChannel rv = delegate.elementAt(index);
		delegate.removeElementAt(index);
		fireIntervalRemoved(this, index, index);
		return rv;
	}

	/**
	 * Removes all of the elements from this list.  The list will
	 * be empty after this call returns (unless it throws an exception).
	 */
	public void clear() {
		int index1 = delegate.size()-1;
		delegate.removeAllElements();
		if (index1 >= 0) {
			fireIntervalRemoved(this, 0, index1);
		}
	}

	/**
	 * Deletes the components at the specified range of indexes.
	 * The removal is inclusive, so specifying a range of (1,5)
	 * removes the component at index 1 and the component at index 5,
	 * as well as all components in between.
	 * <p>
	 * Throws an <code>ArrayIndexOutOfBoundsException</code>
	 * if the index was invalid.
	 * Throws an <code>IllegalArgumentException</code> if
	 * <code>fromIndex &gt; toIndex</code>.
	 *
	 * @param      fromIndex the index of the lower end of the range
	 * @param      toIndex   the index of the upper end of the range
	 * @see	   #remove(int)
	 */
	public void removeRange(int fromIndex, int toIndex) {
		if (fromIndex > toIndex) {
			throw new IllegalArgumentException("fromIndex must be <= toIndex");
		}
		for(int i = toIndex; i >= fromIndex; i--) {
			delegate.removeElementAt(i);
		}
		fireIntervalRemoved(this, fromIndex, toIndex);
	}

}
